/**
 * @file:   main.c
 * @note:   2018-2020, <git.oschina.net/think3r>
 * @brief  转换网易云歌单至 m3u8 通用歌曲列表
 * @author: think3r
 * @date    2018/4/1
 * @note:
 * @note \n History:
   1.日    期: 2018/4/1
     作    者: think3r
     修改历史: 创建文件
   2.日    期: 2018/4/11
     作    者: think3r
     修改历史: 修改文件, 修正一些 bug. add 注释;
   3.日    期  : 2018年4月17日
     作    者  : think3r
     修改内容    : 兼容 windows 平台;

  ****************************** 尚待完成的工程 *******************************
    1. 尚待添加歌曲列表等相关的注释;
        - https://blog.csdn.net/langeldep/article/details/8603045
        - https://en.wikipedia.org/wiki/M3U
    2. 要求能读取网易云音乐昵称(注意汉字的转换);
    3. 简化回掉函数的流程;
    4. 正确统计本地是否具有该歌曲文件, 歌曲数量等;
    5. 能够解析输入的参数(包含额外的歌曲文件夹); 
    6. 后续可进行电脑端网易云数据库的解析工作;
 */

/*----------------------------------------------*/
/*                  头文件包含                  */
/*----------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>  /* 使用 exit() 函数 */
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>  /*使用 命令行参数  getopt*/
#include "common.h"
#include "build_time.h"
#include "sqlite3.h"


/*----------------------------------------------*/
/*                  宏类型定义                  */
/*----------------------------------------------*/
#define PRINT(format,...) printf("*[%s  L:%04d]>   "format"",__FUNCTION__, __LINE__, ##__VA_ARGS__)

/*----------------------------------------------*/
/*                 结构体定义                   */
/*----------------------------------------------*/

/*----------------------------------------------*/
/*                  函数声明                    */
/*----------------------------------------------*/
int out_put_track(char * playListName,  char * playListId, UINT trackCount);

/*----------------------------------------------*/
/*                  全局变量                    */
/*----------------------------------------------*/
sqlite3 *db = NULL;         /* 数据库句柄 */
char DATABASE_PATH[100] = "./cloudmusic.db";  /* 数据库文件存放路径         */
char DATABASE_CMD[200] = {0};
char M3U8_PATH[100] = {0};
/* 网易晕音乐下载歌曲文件(只有歌曲名字)存放位置, 注意:
        非网易云下载的本地歌曲数据库中的 path 为全路径 */
char OUT_PUT_DIR_PATH_LINUX[200] = "./output/";
char OUT_PUT_DIR_PATH_WIN[200] = ".\\output\\";
char FIX_MUSIC_PATH_HEAD[200] = "../netease/cloudmusic/Music/";
char FIX_MUSIC_PATH_HEAD_HALF[] = "storage";
char USER_NAME[200] = "逝者";

UINT RealMusicNumber_inPlayList = 0;
char helpInfo[] = 
        "\t[-h]   \tPrint help Info\n"
        "\t[-u]   \tSet user name of CloundMusic\n"
//        "\t[-o]   \tSet outPut directory Path\n"
        "\t[-f]   \tSet file path of cloundmusic.db\n"
        "\t[....]\n";
/*----------------------------------------------*/
/*                  函数定义                    */
/*----------------------------------------------*/

/**
 * @function:   print_record
 * @brief:      sqlite3 查询回掉函数, 打印查询的值;
 *                 在本函数中不要添加其余函数, 否则会在二次调用时造成递归;
 * @param[in]:  void *params          sqlite3_exec() 函数的第四个返回值;
 * @param[in]:  int n_column          返回结果的列数;
 * @param[in]:  char **column_value   该列的值;
 * @param[in]:  char **column_name    该列的名字;
 * @param[out]: None
 * @return:     int
 */
int print_record(void *params, int n_column, char **column_value,char **column_name)
{
    int i = 0;
    static UINT in_times = 0;
    
    if( 0 == n_column )
    {
        printf("querry filed\n");
    }
    
    for(i =  0; i < n_column; i++)
    {
        printf("[%02d] %s:[%s]  ", in_times, column_name[i], column_value[i]);
    }
    
    printf("\n");

    in_times++;

    return 0;
}

/**
 * @function:   creatOutPutDir
 * @brief:      创建输出文件夹
 * @param[in]:  void  
 * @param[out]: None
 * @return:     ERR
 */
ERR creatOutPutDir( void )
{
    ERR err = ok;
  
    if(access(OUT_PUT_DIR_PATH_LINUX, W_OK)==-1)/*通过 access 函数是查看文件夹是不是存在 */
    {               
    
#if defined(_WIN32) || defined(WIN32)
       char cmd[30] = {0};
       sprintf(cmd,"md %s", OUT_PUT_DIR_PATH_WIN);
//     printf("cmd: [%s]\n", cmd);

       if(system(cmd))
#else
       if(mkdir(OUT_PUT_DIR_PATH_LINUX, 0777))
#endif       
       {
           printf("creat dir  [%s]  failed!!!\n", OUT_PUT_DIR_PATH_LINUX);        
       }
       else
       {
           printf("creat dir  [%s]  success!!!\n", OUT_PUT_DIR_PATH_LINUX);       
       }
       
       err = FILE_NOT_EXIT;
    }
    else
    {
        printf("dir [%s] already exits!\n\n", OUT_PUT_DIR_PATH_LINUX);
    }
    
    return err;
}

/**
 * @function:   open_cloudMusic_Database
 * @brief:      打开网易云音乐的数据库
 * @param[in]:  void  
 * @param[out]: None
 * @return:     ERR
 */
ERR open_cloudMusic_Database(void)
{
    ERR ret = OK;      

    /* 打开数据库*/
    ret = sqlite3_open( DATABASE_PATH , &db);  
    if (SQLITE_OK  == ret )
    {
        printf("\n open database: "UNDERLINE" [%s] "RESET_COLOR " success!\n", DATABASE_PATH);  
    }
    else
    {  
        /*  fprintf函数格式化输出错误信息到指定的stderr文件流中  */  
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));//sqlite3_errmsg(db)
        sqlite3_close(db);  
        exit(1);  
    }
    
    return ok;
}


/**
 * @function:   CallBack_outPutPlayList
 * @brief:      找到歌曲列表
 * @param[in]:  void *params         
 * @param[in]:  int n_column         
 * @param[in]:  char **column_value  
 * @param[in]:  char **column_name   
 * @param[out]: None
 * @return:     ERR
 */
int CallBack_outPutPlayList(void *params, int n_column, char **column_value,char **column_name)
{
    UINT music_count = 0;
    static UINT in_times = 0;    
    in_times++;
    UINT Local_RealMusicNumber_inPlayList = 0;

    if( 0 == n_column || NULL == column_name)
    {
        return PARAM_INVALID;
    }
    
    music_count = strtol(column_value[2], NULL, 0);

    Local_RealMusicNumber_inPlayList = 
            out_put_track(column_value[0],  column_value[1], music_count);
    
    printf("(%02d) - Play List File [%s] Creat!  \n", in_times, M3U8_PATH );
    printf("     |---> Total_in_database:[%d],  find:[%d];\n", music_count, Local_RealMusicNumber_inPlayList);


    /*清空下层调用时写入的命令,以免最终返回时 sqlite3 接口报错;*/
    memset(DATABASE_CMD, 0x00, sizeof(DATABASE_CMD));
    
    return ok;
}



int CallBack_outPut_track(void *params, int n_column, char **column_value,char **column_name)
{
    FILE *in_file = NULL;
    
    if( 0 == n_column || NULL == column_name || NULL == params)
    {
        return PARAM_INVALID;
    }

    RealMusicNumber_inPlayList ++;
    
    in_file = (FILE *) params;

    if( NULL == strstr(column_value[1], FIX_MUSIC_PATH_HEAD_HALF))    /* 添加固定歌曲文件路径 */
    {
        fwrite(FIX_MUSIC_PATH_HEAD, strlen(FIX_MUSIC_PATH_HEAD), 1, in_file);
    }   

    fwrite( column_value[1], strlen(column_value[1]), 1, in_file );
    fwrite("\n", strlen("\n"), 1, in_file);
    
    return ok;
}


/**
 * @function:   find_musicPlayList
 * @brief:      找到对应用户的歌曲列表
 * @param[in]:  None
 * @param[out]: None
 * @return:     UINT
 */
UINT find_musicPlayList(void)
{
    UINT ret = OK;      
    char *errmsg = NULL;


    sprintf( DATABASE_CMD, "select _id,name,track_count from playlist "
                       " where creator_nickname LIKE '%%%s%%';", USER_NAME);
    
    /* 通过回掉函数来查询 */
    ret = sqlite3_exec(db, DATABASE_CMD, CallBack_outPutPlayList, NULL, &errmsg);    
    if(ret != SQLITE_OK)
    {
        PRINT("err!!   %s\n", errmsg);
    }
    
    return ret;
}

/**
 * @function:   out_put_track
 * @brief:      创建相应歌单, 并调用下层函数填充歌单内容;
 * @param[in]:  char * playListId    
 * @param[in]:  char * playListName  
 * @param[in]:  UINT trackCount      
 * @param[out]: None
 * @return:     在数据库中找到的对应歌单歌曲数量;
 */
int out_put_track( char * playListId, char * playListName,  UINT trackCount)
{
    FILE *outfile = NULL;  
    ERR err = OK;      
    char *errmsg = NULL;           
    
    if( NULL == playListId || NULL == playListName || 0 == trackCount)
    {
        return PARAM_INVALID;
    }

    RealMusicNumber_inPlayList = 0;
    
//    printf("\t ***| %s, | %s, | %d |\n",  playListName , playListId, trackCount);
    
    sprintf(M3U8_PATH, "./output/%s.m3u8", playListName);

/*windows 平台下, 文件名编码为 UTF-16 */
#if defined(_WIN32) || defined(WIN32)  
    char * file_path;
    file_path = sqlite3_win32_utf8_to_mbcs_v2(M3U8_PATH);
    memset(M3U8_PATH, 0x00, sizeof(M3U8_PATH));
    memcpy(M3U8_PATH, file_path, strlen(file_path));
#endif
    
    if((outfile = fopen(M3U8_PATH,"w+"))==NULL)  
    {  
        printf( "\nCan not open the path: %s \n", M3U8_PATH); 
        
        err = FILE_NOT_EXIT;
        return err;
    }

    memset(DATABASE_CMD, 0x00, sizeof(DATABASE_CMD));
    

    sprintf(DATABASE_CMD,  "select id,path from  local_track  where musicId IN " 
                                " (select track_id from playlist_track "
                                    " where playlist_id like %s "
                                        " ORDER BY track_order ASC)", 
                                        playListId);
                           
    err = sqlite3_exec(db, DATABASE_CMD, CallBack_outPut_track, (void *)outfile, &errmsg);    
    if(err != SQLITE_OK)
    {
        PRINT("err!!   %s\n", errmsg);
        return err;
    }  
    
    fclose(outfile);
    
    return     RealMusicNumber_inPlayList;
}

/**
 * @function:   decodeArgV
 * @brief:      解析传递进来的命令行参数
 * @param[in]:  int argc      
 * @param[in]:  char *argv[]  
 * @param[out]: None
 * @return:     
 */
ERR decodeArgV(int argc, char *argv[])
{    
    int opt = 0;  
    char deCodeFromate[] = "hu:";    
	extern char * optarg;

#if 0    //打印输入参数
    UINT i = 0;

    printf("argc:%d\r\n", argc);    

    for ( i = 0 ; i < argc ; i++ )
    {
        printf("argv%d:[%s]\n", i, argv[i]);
    }
#endif   /* end of (#if 0  //打印输入参数) */
    
    while((opt = getopt(argc, argv, deCodeFromate)) != -1)
    {  
         switch(opt)
         {
            case 'h':  
                printf("\n%s\n",helpInfo);  
                exit(0);
            case 'u':
                  if ( strlen(optarg) > sizeof(USER_NAME) )
                  {
                      printf(" your input Parameter [user name] is too long!!\n");
                      exit(1);
                  }
                  else
                  {
               #if defined(_WIN32) || defined(WIN32)  
                     char * dataBasePath;
                     dataBasePath = sqlite3_win32_mbcs_to_utf8_v2(optarg);                     
                     strcpy(optarg, dataBasePath);
               #endif
                      memset(USER_NAME, 0x00, sizeof(USER_NAME));
                      strcpy(USER_NAME, optarg);
                      printf(" Set user name success: [%s].\n", USER_NAME);
                  }                  
                  break;
              case 'f':
                  if ( strlen(optarg) > sizeof(DATABASE_PATH) )
                  {
                      printf(" your input Parameter [dataBase path] is too long!!\n");                      
                      exit(1);
                  }
                  else
                  {
                  /*windows 平台下, 读入汉字为 UTF-16 编码, 需转换成 UTF-8 ; */
               #if defined(_WIN32) || defined(WIN32)  
                     char * userName;
                     userName = sqlite3_win32_mbcs_to_utf8_v2(optarg);
                     strcpy(optarg, userName);
               #endif
                      memset(DATABASE_PATH, 0x00, sizeof(DATABASE_PATH));
                      strcpy(DATABASE_PATH, optarg);
                      printf(" Set dataBase Path success: [%s].\n", DATABASE_PATH);
                  }
                  break;
            default:  
                printf("Undefine Command!!\n");                  
                exit(1);
         }  
    }  

    return ok;
}


/**
 * @function:   main
 * @brief:      主函数
 * @param[in]:  void  
 * @param[out]: None
 * @return:     int
 */
int main(int argc, char *argv[])
{
    UINT ret = OK;      

    /* 生成编译日期 */
    mk_Build_Date(&build_time);

    decodeArgV(argc, argv);

    open_cloudMusic_Database();
    
    creatOutPutDir();

    find_musicPlayList();
    
    ret = sqlite3_close(db);

#if defined(_WIN32) || defined(WIN32)
    system("pause");
#endif

    return ret;
}


void test_sqlite(void)
{
    UINT ret = OK;      
    char *errmsg = NULL;

    /* 查询创建的歌单 */
    sprintf( DATABASE_CMD, "select _id,name,track_count from playlist "
                           " where creator_nickname LIKE '%%%s%%';", USER_NAME);
    
#if 1   /* 两种不同的查询方式 */
    /* 通过回掉函数来查询 */
    ret = sqlite3_exec(db, DATABASE_CMD, CallBack_outPutPlayList, NULL, &errmsg);    
    if(ret != SQLITE_OK)
    {
        PRINT("err!!   %s\n", errmsg);
    }
#else
    UINT index = 0, i = 0, j = 0
    int nrow = 0, ncolumn = 0; 
    char **dbresult = NULL ;
    
    /* 通过 get_table 来查询;       dbresult[0][Idx] 为字段名 */
    ret = sqlite3_get_table(db,
                            DATABASE_CMD,
                            &dbresult,
                            &nrow,
                            &ncolumn,
                            &errmsg); 
    if(SQLITE_OK == ret )
    {
        printf("query %i records.\n",nrow);
        
        for(i = 0; i < nrow; i++) /* 行 */
        {            
            printf("[%2i]",i);                                  
            
            for(j = 0; j < ncolumn; j++) /* 列 */
            {
                printf(" %s",dbresult[index]);
                index++;
            }
            printf("\n");

            out_put_track(dbresult[0 + ncolumn * i], 
                            dbresult[1 + ncolumn * i], 
                            strtol(dbresult[2 + ncolumn * i], NULL, 0));
        }
    }
    else
    {   
        PRINT(" %s\n", errmsg);
    }
    sqlite3_free_table(dbresult);
#endif
}
